2012/08/09

Recent entries from same category

  1. Go 言語プログラミングエッセンスという本を書きました。
  2. errors.Join が入った。
  3. unsafe.StringData、unsafe.String、unsafe.SliceData が入った。
  4. Re: Go言語で画像ファイルか確認してみる
  5. net/url に JoinPath が入った。

某SNSでFizzBuzzが再燃しておりますが、面接時の足きり尺度としてFizzBuzzが使えるかどうかは別として、コードとしてFizzBuzzは有益でもなんでも無いので、FizzBuzzを題材としたコードを晒してみる。 Go言語には、元々Gobと呼ばれるエンコーダが同梱されていて、Goで扱える型をストリーム上に垂れ流しデコードする事が出来る。かつ、RPCも用意されているので小規模なクラサバならば簡単に書けてしまう。
巷のRPCは、エンティティからスタブを作ったり面倒だったりしますが、Go言語ではクライアントとサーバの意識さえ合っていればスタブも必要ない。
まずサーバ package main

import (
    "fmt"
    "net"
    "net/rpc"
)

type FizzBuzz int

func (fb *FizzBuzz) Serve(n int, r *stringerror {
    switch {
    case n % 15 == 0:
        *r = "FizzBuzz"
    case n % 3 == 0:
        *r = "Fizz"
    case n % 5 == 0:
        *r = "Buzz"
    default:
        *r = fmt.Sprintf("%d", n)
    }
    return nil
}

func main(){
    fb := new(FizzBuzz)
    rpc.Register(fb)

    server, _ := net.Listen("tcp"":8001")
    for {
        client, err := server.Accept()
        if err != nil {
            println(err.Error())
            continue
        }
        rpc.ServeConn(client)
    }
}
Register に渡される型のメソッドで、入力引数、戻り値のアドレス の二つを引数に持ち、戻り値に error を持つ物はエクスポートされる。なのでこの場合、n が FizzBuzz の入力、r が戻り値へのアドレスになる。
尚、このRPCは透過性があるので、別に TCP でなくても構わない。

次にクライアント。型がサーバと一致しているのなら、とても簡単に呼び出せる。 package main

import (
    "net/rpc"
)

func main() {
    client, _ := rpc.Dial("tcp""localhost:8001")
    var ret string
    for i := 1; i <= 100; i++ {
        client.Call("FizzBuzz.Serve", i, &ret)
        println(ret)
    }
}
GobというGo言語に特化したエンコーダなので、他の言語でも...という訳には行かないが、ちょっとした物を作る時にはかなり便利である。
尚、Go言語には net/rpc/jsonrpc も含まれていて、こちらを使えばポータビリティのある RPC が書ける様になっている。
Posted at by